/*
 * Copyright (c) 2003-2010, Live Media Pty. Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *  3. Neither the name of Live Media nor the names of its contributors may be used to endorse
 *     or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
#include <inttypes.h>
#include <stdbool.h>
#include <endian.h>
#include <stdio.h>

#include "buffers.h"
#include "type-protocol.h"
#include "dictionary.h"
#include "common.h"
#include "argot-rpc.h"

static bool mcmp( char * d1, char * d2, int len )
{
	TRACE("MCMP LEN = %i",len);

	do
	{
		TRACE("MCMP %i,%i,%i",*d1,*d2,len);
		if (*d1++ != *d2++) return false;
	}
	while( --len > 0 );
	
	return true;
}

static int find_name( char * location, uint16_t len )
{
	int x;
	int id = -1;
	
	TRACE( "len %i", len );

	for ( x = 0 ; x < library.size ; x++ )
	{

		if ( *library.entries[x].name.name == TP_DICTIONARY_NAME || 
		     *library.entries[x].name.name == TP_DICTIONARY_BASE )
		{
			if ( library.entries[x].name.length == len )
			{
				TRACE( "comparing name|base - %i", x );
			
				if ( mcmp( library.entries[x].name.name, location, len ) )
				{
					id = x;
					break;
				}
			}
		}
		else if ( *library.entries[x].name.name == TP_DICTIONARY_DEFINITION )
		{
			if ( library.entries[x].name.length-2 == len )
			{
				TRACE( "comparing definition - %i", x );
				char * l = library.entries[x].name.name;
				if ( mcmp( l+1, location+1, len-2 ) )
				{
					id = x;
					break;
				}
			}
		}
		else if ( *library.entries[x].name.name == TP_DICTIONARY_RELATION )
		{
			
		}
		else
		{
			TRACE("Unknown location type in dictionary");
		}
	}

	TRACE( 	"RETURNING %i", id );

	return id;
}

void write_header( int msgType )
{
	send_clear();
	send_u8( msgType );
}

static int find_location( char * location, uint16_t len )
{
	int x;
	int id = -1;
	
	TRACE( "len %i", len );

	for ( x = 0 ; x < library.size ; x++ )
	{
		if ( library.entries[x].name.length == len )
		{
			TRACE( "comparing location - %i", x );
			
			if ( mcmp( library.entries[x].name.name, location, len ) )
			{
				id = x;
				break;
			}
		}
	}

	return id;
}


static int processMap()
{
	uint16_t location_length;
	char * location_data;
	uint16_t definition_length;
	char * definition_data;
	int id;
	
	TRACE("**PROCESS MAP");
	
	location_length = read_u16();
	location_data = read_data( location_length );

	definition_length = read_u16();
	definition_data = read_data( definition_length );

	TRACE("MAP ID LOCLEN %i, DATALEN %i", location_length, definition_length );

	id = find_location( location_data, location_length );    
	
	TRACE("MAP ID %i DATALEN %i", id, definition_length );
	
	if ( id != -1 )
	{
		if ( !mcmp( library.entries[id].definition.data, definition_data, definition_length ) )
		{
			TRACE("DATA NOT SAME");			
			TRACE("LOCAL");
			PRINT_DATA( library.entries[id].definition.data, definition_length );
			TRACE("REMOTE");
			PRINT_DATA( definition_data, definition_length );
			
			id = -1;
		}

	}

	TRACE("MAP RESULT %i",id);


	write_header( TYPE_MAP );
	send_u32( id );	
	
	return PROCESS_OK;
}

static int processMapDefault()
{
	int id;
	uint16_t location_length;
	char * location_data;

	TRACE("PROCESS MAP DEFAULT");

	location_length = read_u16();
	location_data = read_data( location_length );

	id = find_name( location_data, location_length );

	if (id == -1)
	{
		write_header( TYPE_ERROR );
	}
	else
	{
		write_header( TYPE_MAPDEF );
		send_u32( id );

		//send_u16( library.entries[id].name.length );
		send_write( library.entries[id].name.name, library.entries[id].name.length );

		send_u16( library.entries[id].definition.length );
		send_write( library.entries[id].definition.data, library.entries[id].definition.length );
	}
	return PROCESS_OK;	
}

static int processMapReserve()
{
	uint8_t len;
	char * name;
	int32_t id;
	
	name = read_u8ascii( &len );	
	TRACE("**MAP RESERVE %i, '%s'", len, name );
	
	id = find_name( name, len );
	
	write_header( TYPE_MAPRES );
	send_u32( id );
	return PROCESS_OK;
}

static int processMapReverse()
{
	int32_t id = read_u32();
	int length = 0;
	
	TRACE( "**MAP REVERSE %i", id );

	length =  library.entries[id].name.length + 1;
	length += library.entries[id].definition.length + 4;
	
	write_header( TYPE_MAPREV );
	send_u8( library.entries[id].name.length );
	send_write( library.entries[id].name.name, library.entries[id].name.length );
	send_u32( library.entries[id].definition.length );
	send_write( library.entries[id].definition.data, library.entries[id].definition.length );
	
	return PROCESS_OK;
}

static int processGetObjectBase()
{
    TRACE("**GET OBJECT BASE");
    
    write_header( TYPE_BASE );
    send_u8( 1 ); 
    send_write( base_object.data, base_object.length );
    
    return PROCESS_OK;
}

int processTypeMessage( int msgType, int size )
{
    TRACE("processTypeMessage");
    switch ( msgType )
    {
    case TYPE_MAP:
    	return processMap();
    case TYPE_MAPDEF:
	return processMapDefault();
    case TYPE_MAPRES:
    	return processMapReserve();
    case TYPE_MAPREV:
    	return processMapReverse();
    case TYPE_BASE:
    	return processGetObjectBase();
    case TYPE_MSG:
    	return processRpcReq();
    default:
    	TRACE("NOT IMPLEMENTED %i", msgType );
    	return PROCESS_ERROR;
    }
}
